home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Pascal Super Library
/
Pascal Super Library (CW International)(1997).bin
/
LIBRARY
/
SLTPU70C
/
SLTPU.REF
< prev
next >
Wrap
Text File
|
1993-09-16
|
78KB
|
1,993 lines
Searchlight BBS Programmer's Library
Function Reference
Version 3.5, September, 1993
(c) Copyright 1993 Searchlight Software. All Rights Reserved.
-----------------------------------------------------------------------------
Unit: FILEDEF.TPU
Depends On: Dos,Block,Multi
-----------------------------------------------------------------------------
Description:
Contains procedures for opening the CONFIG, NODES and CHAT files, and
reading and writing records from these files. The OPENFILES procedure
automatically initializes several key variables when opening the CONFIG
file, and is the recommended way of initializing any program.
FILEDEF.TPU also contains the type definitions used in SLBBS data files.
Please see FILEDEF.REF for a listing of the type definitions.
Procedures, Functions and Variables:
type filetype = (CONFIGF,NODESF,CHATF);
fileset = set of filetype;
var configpath: string[60] = '';
Function OpenFiles (arg: fileset): boolean;
The procedure OpenFiles, combined with the type definitions shown, will
initialize the system by opening CONFIG.SL2, and optionally NODES.SL2 and
CHAT.SL2. Call OpenFiles with an argument of type fileset, depending on
which files you wish to open. The return value is TRUE if the files were
successfully opened, and FALSE if an error occured.
Example:
if OpenFiles([CONFIGF,NODESF]) then begin
{ main program here }
else
writeln('Error-- Could not find CONFIG file!');
OpenFiles needs to know the path to CONFIG.SL2 if it is not in the current
directory. You can specify this path by setting the Configpath variable
before calling the OpenFiles procedure. Programs typically obtain this
value from command line arguments.
Example:
Configpath:=ParamStr(1); { Assume 1st commandline parameter is path }
if OpenFiles([CONFIGF,NODESF]) ...
If 'Configpath' is not set and CONFIG.SL2 is not in the current directory,
OpenFiles searches for an environment variable called SLBBS and uses that
string as the path to CONFIG.SL2, if it exists. Users can set up the
environment with a DOS command such as "SET SLBBS=C:\BBS".
Only the CONFIG path need be specified. OpenFiles automatically uses the
'Data Files Path' in the CONFIG file to locate the other files.
The CHAT.SL2 file is valid only for multiuser systems. It is suggested that
you not attempt to open this file until you verify that more than 1 node is
operating. This can be detected by examining the value of 'MaxNode' (see
below) after opening the CONFIG file.
Example:
ok:=OpenFiles([CONFIGF,NODESF]); { ok = boolean variable }
if ok and (maxnode>1)
then ok:=OpenFiles([CHATF]);
It is recommended that the CONFIG and NODES file be opened at the beginning
of every program and maintained for the life of the program. The CHAT file
need not be opened unless it is required for the application.
var cf: configtype; { Global CONFIG Record }
node,maxnode: integer; { Global Node Info }
isopen: array[CONFIGF..CHATF]
of boolean;
A global variable 'Cf' is declared in the Filedef unit and can be used
throughout an application to obtain information about the current system
configuration (see FILEDEF.REF for the record layout of this variable). 'Cf'
is automatically initialized to the contents of the CONFIG.SL2 file when
that file is opened by OpenFiles.
'Node' and 'Maxnode' reflect the current node number and the maximum node
number for the system, respectively.
The 'Isopen' structure is an array of booleans that will let you determine
later in your program which files are open. For example:
if Isopen[CHATF]
then { run chat functions }
Procedure ReadCf;
Procedure WriteCf;
These procedures read or write the global variable 'Cf' to the CONFIG.SL2
file. 'ReadCf' is not generally required since Cf is read into memory at
the start of the program. If your program changes the value of a variable
in 'Cf', call 'WriteCf' to update it to disk.
Note: The CONFIG.SL2 file should always be private to any program which
manipulates it. Therefore, there is no need to observe record locking
techniques when updating this file.
Function NodeFileSize: integer;
Procedure ReadNode (var n: nodetype; r: integer);
Procedure WriteNode (var n: nodetype; r: integer);
Function 'NodeFileSize' returns the size in records of NODES.SL2 (which
should always be equal to the value of 'Maxnode' plus 1). Procedure
'ReadNode' reads a variable of type 'Nodetype' from the NODES.SL2 file at
the given record number ('r') which should be between 0 and Maxnode. You
can use this procedure to obtain information about the other nodes on a
multiuser system. If you wish to update the nodes file, 'Writenode' will
write a node record back to disk.
Procedure CloseAllFiles;
This procedure closes the CONFIG, NODES and CHAT file if they are open. It
should be called by a program before terminating. Note that in spite of its
name, 'CloseAllFiles' does not close the USER file, or any open SUBBOARD or
FILE DIRECTORY files; be sure to close those files, too.
-----------------------------------------------------------------------------
Unit: BLOCK.TPU
Depends On: Dos,Multi
-----------------------------------------------------------------------------
Description:
The BLOCK unit contains a number of low-level procedures used by other units
in the library. Only a few of its procedures and functions are generally
needed directly by application programs.
Procedures, Functions and Variables:
BlockFileType = record { ram file info }
filevar: file; { dos file var }
open: boolean; { set if file is open }
lock: integer; { file lock count }
recsize: word; { record size }
offset: longint; { offset to 1st record }
header: fileheader; { header info }
hlock: integer; { header locked? }
end;
The 'BlockFileType' variable is used by the MESSAGE, USERS, MEMBERS and DIR
units as file variables for the Searchlight data files managed by these
units. We present the above type definition for your information.
Application programs should NOT need to declare variables of this type,
although you should be aware that the variable 'UserFile' (USERS.TPU) and
the file components of 'MainSub' (MESSAGES.TPU) and 'MainDir' (DIR.TPU) are
declared as type 'BlockFileType'. If necessary, you can access information
such as the record size, open status, and locked status of these files in
the record fields; however, do not attempt to change any of these fields
yourself.
Function SizeInRecs (var f: blockfiletype): longint;
This function returns the size in records of a given 'BlockFileType'. Valid
parameters for this function are variables such as 'UserFile' (Searchlight
user file) or 'Mainsub.Headerf' (Main subboard header file). The function
returns the TOTAL number of blocks in the file, which includes deleted
records. This function can be used by programs that process files
sequentially; be sure to ignore deleted records (deleted records will have
the value $FF in the first byte of the record).
Procedure ReadBlockfile (var f: blockfiletype; r: longint; p: pointer);
Procedure WriteBlockfile (var f: blockfiletype; r: longint; p: pointer);
These two functions read and write any record from any Searchlight data file
that is currently open; for example, the Userfile. These functions are low
level in nature and will only rarely be used by application programs. The
program must insure that the pointer value passed contains enough room to
read a variable of the correct size from the file.
Example:
ReadBlockFile(Userfile,17,@User);
Reads the 17th record from the user file into variable 'User', which is
presumably of type 'UserType' (although no type checking is performed).
See LockFile and UnlockFile below for more information.
Function LockFile (var f:blockfiletype): boolean;
Procedure UnLockFile (var f:blockfiletype);
These procedures implement DOS level file locking, allowing programs to
update shared files on multiuser systems without corruption. The 'LockFile'
and 'UnlockFile' procedures are rarely required by application programs,
since all of the high-level functions provided in this library (ie.
'AddUser' in USERS.TPU, 'MsgPost' in MESSAGE.TPU) automatically lock and
unlock files as needed.
The only time you need to call these procedures directly is if your program
needs to update a record in a shared file (ie. any file other than the
CONFIG file) directly. The correct way to do this is:
(1) LOCK the specified file
(2) READ the record from the file that requires updating
(3) UPDATE the record in memory
(4) WRITE the updated record back to disk
(5) UNLOCK the file
It is important that the total time that a file is LOCKed be as short as
possible, since LOCKing a file prevents other nodes from updating it. You
should also be aware that if you call 'LockFile' to lock a file and that
file is already locked, 'LockFile' will wait up to 45 seconds for the file
to become unlocked before returning with a value of FALSE to indicate that
the lock attempt failed. Programs should always check the value returned by
'LockFile' and report an error if it is false.
This example updates the number of times read ('Rd') variable in a message
header in the HEADER file of a subboard. Assume that we already know the
value of the header's record number (RecNum) and that 'Header' is of type
'HeaderType'. 'ReadBlockFile' is defined as above.
if LockFile(Mainsub.Headerf) then begin
ReadBlockFile(Mainsub.Headerf,RecNum,@Header);
Inc(Header.Rd);
WriteBlockFile(Mainsub.Headerf,RecNum,@Header);
UnLockFile(Mainsub.Headerf);
end;
Notice that the updating procedure is skipped if the lock fails (this will
rarely happen unless a serious error occurs) and that the file remains
locked for just long enough as is necessary to perform the update. Also,
notice that the record is read into memory before being updated. This is
required, even if the record had already been in memory, since another node
might have updated it between the time it was first read and the time the
file was locked.
IMPORTANT -- Do not try to update they key (indexed) field of any record
simply by rewriting the record! This includes the 'Name' field of user,
member and directory records, the date field of directory records, etc.
Updating key fields requires insertion of a new record and deletion of the
old record, via the insert/delete commands provided for the various files.
Calls to 'Lockfile' and 'Unlockfile' for the same file can be nested without
ill effects. For example, if this code fragment is executed:
if Lockfile(Userfile) then begin
{ instructions here }
if Lockfile(Userfile) then begin
{ more instructions here }
UnlockFile(Userfile);
end;
UnlockFile(Userfile);
end;
Only the first call to LockFile actually locks the user file, and only the
last call to UnlockFile actually unlocks the user file. The nested calls
are ignored. This behavior allows procedures which call the Lock and
Unlock functions to work correctly, even if those procedures are called
while the file is already locked.
On a single user system-- even a system where a multiuser version of
Searchlight is in use, but only one node is active-- calls to LockFile and
UnLockFile have NO EFFECT. Therefore, programs should ALWAYS call these
functions when updating a file; they will be effectively ignored if no file
locking is necessary.
-----------------------------------------------------------------------------
Unit: GENERAL.TPU
Depends On: Dos,Filedef
-----------------------------------------------------------------------------
Description:
The GENERAL unit contains some helpful functions for processing SLBBS time
and date types, as well as some general purpose functions which are used by
the other units in the library and may be called by application programs.
Procedures, Functions and Variables:
Function Min (x,y:integer): integer;
Function Max (x,y:integer): integer;
These are just the classic Min and Max functions; Min returns the smaller of
its two arguments, and Max returns the larger. Useful in a variety of
situations where limits need to be enforced.
Procedure Upstr (var s: string); { Conv string to uppercase }
Procedure TruncSpaces (var s: string); { Strip trailing spaces }
Procedure StripSpaces (var s: string); { Strip leading & trailing spaces }
String conversion procedures. 'Upstr' converts a string to all uppercase.
'TruncSpaces' strips a string of any trailing spaces, and 'StripSpaces'
strips both leading and trailing spaces.
Procedure Dosdate (var d: datetype);
Procedure Dostime (var t: timetype);
These functions return the current date and time in the format used by
Searchlight data files.
Example:
Dosdate(header.date);
Dostime(header.time);
Assuming 'Header' is a record of type 'Headertype', these two function
calls will initialize the header to the current date and time.
Function SecOffset: longint;
Function ElapsedFrom (t: longint): longint;
Used together, these functions let you time events to an accuracy of about 2
seconds. Call 'SecOffset' at the beginning of an event and save its value
in a variable. Call 'ElapsedFrom' with that saved value later to find out
how many seconds have passed since the call to SecOffset.
Example:
starttime:=SecOffset;
repeat
{ time consuming function here }
until ElapsedFrom(starttime)>=30; { loop for 30 seconds }
Function StrDate (d: datetype): string;
This function returns the date in the argument as an 8 character string in
the format 'MM-DD-YY', or 'DD/MM/YY' if European date format is selected in
Searchlight.
Function DateDiff (d1,d2: datetype): integer;
The 'DateDiff' function returns the number of days between two dates, 'd1'
and 'd2'. If 'd1' is a later date than 'd2', then the result will be
positive. Otherwise, the result is negative. This function is useful for
comparing dates in messages or user files to a target date.
Function TimeDiff (t1,t2: timetype): integer;
Returns the difference between 2 times, in minutes. This function works
correctly even if midnight has passed between the start time and the current
time.
Procedure Hash (s: string; var r: pwtype);
This procedure transforms a password string into a 3 byte password hashcode
(pwtype) used in the USER file and file directory files. You can use this
procedure to change file or user passwords or to install passwords for new
users or files.
The input string ('S') should be an all-caps string with no leading or
trailing spaces (embedded spaces are OK).
Example:
Hash('SHIPYARD',User.Passwd); { Change user's password to SHIPYARD }
Function Nullpw (r: pwtype): boolean;
This function takes a password hashcode as input, and returns TRUE if the
password is blank. You can use this to check for a blank password before
asking a user to enter a password.
Example:
if not NullPw(User.Passwd)
then { input password from user }
else { skip password input }
Note: Blank passwords simply contain 3 bytes of binary zero. To create a
blank password, simply fill the password (Pwtype) with zero. Example:
Fillchar(User.Passwd,sizeof(pwtype),0); { blank user's password }
Function CheckPasswd (passwd: pwtype; passtr: string): boolean;
This function can be used to check whether a password string which was input
by a user matches a stored encoded password. You can use this function
to check passwords in the USER file or in directory files. The result is
true if the password matches, false otherwise.
Note that password strings should be all capital letters and should not
contain any leading or trailing spaces, although embedded spaces are allowed.
Example:
input(passtr); { input password from user }
upstr(passtr); { see GENERAL.TPU }
stripspaces(passtr); { see GENERAL.TPU }
if CheckPasswd(DirRecord.passwd,passtr)
then writeln('Password correct')
else writeln('Incorrect password!');
-----------------------------------------------------------------------------
Unit: USERS.TPU
Depends On: Block,Filedef,Tree
-----------------------------------------------------------------------------
Description:
This unit contains the main procedures required to access the Searchlight
USER file, including procedures to search for, insert, and delete users.
Procedures, Functions and Variables:
var User: Usertype;
This global variable of type 'Usertype' should always be used to store
information about the current user. It is not initialized; to do that, you
should call
Readuser(user,cf.curruser)
after opening the User file. It is recommended that you use this global
variable exclusively to process the current user. If you program requires
access to other user records, declare other variables of type UserType and
use them locally.
var UserFile: BlockFileType;
This is the file variable that contains the user file. It is normally not
required to refer to this variable, except when performing low level
operations such as file lock and unlock (see BLOCK unit).
Function OpenUserFile: boolean;
Procedure CloseUserFile;
The user file needs to be explicitly opened before any other procedures in
this unit may be used, and should be closed when the program terminates.
Open the user file after the CONFIG file has been opened; 'OpenUserFile'
will use the data path stored in CONFIG. The function returns TRUE if the
user file is successfully opened, FALSE if there was an error.
Example:
if OpenFiles([CONFIGF,NODESF]) and OpenUserFile then begin
{ main program here}
CloseUserFile;
CloseAllFiles;
end;
It is recommended that the user file be opened if you plan to save or
delete messages.
Procedure ReadUserHeader (var header: userheader);
Procedure WriteUserHeader (var header: userheader);
These functions read and write the header information at the beginning of
the user file (type 'UserHeader'). The header contains the netmail setup
information used in Searchlight 3.0 and later versions.
Procedure ReadUser (var u: usertype; n: longint);
Procedure WriteUser (var u: usertype; n: longint);
These functions read or write a record from the User file (which must be
open). 'U' contains a record of usertype, and 'N' is the desired record
number. Calling ReadUser and WriteUser is identical to calling the low
level functions 'BlockRead' and 'BlockWrite' (BLOCK.TPU) except that these
functions are more convenient and provide type checking.
'WriteUser' should be used with care; see the record locking techniques in
BLOCK.TPU for more information. Do not attempt to update the user name
by rewriting the record with a different name.
Procedure Usearch ( key: string; { name to look for }
var urec: usertype; { resulting user record }
var upos: longint); { resulting record number }
Procedure 'Userch' searches for a record in the user file by name. On
entry, 'Key' contains the name of the user you are searching for. 'Key'
should be all-uppercase, and should not contain any leading or trailing
spaces.
On return, if the user is found, 'Urec' contains the user record for the
user and 'Upos' contains the record number where the user was found in the
User file. If the user could not be found, Upos equals zero.
Example:
Usearch('SYSOP',User,UserRec);
if (UserRec=0) then writeln('Cannot find SYSOP Account!');
Procedure AddUser (var newuser: usertype; var result: longint);
This procedure adds a user to the user file. 'Newuser' is a user record
which should be initialized with the user name (MUST be all-caps, with no
leading or trailing spaces!) and other information (ie. location, phone
number, date of first login, etc). The return value 'Result' contains the
record number where the new user record was stored if the function was
successful. If the function fails, Result = 0.
The most common reason why this function might fail is if the user you are
trying to add is already on file.
Note: be sure to initialize the Passwd field of a user record before adding
the user; otherwise, the user may have a random password. See procedure
'Hash' in GENERAL.TPU for more information.
Function DeleteUser (key: string): boolean;
Deletes the given username from the user file. As with Usearch and AddUser,
the name must be all caps with no leading or trailing spaces.
The result of the function is TRUE if the user was successfully deleted, or
FALSE if some error occured (such as if the user could not be found).
Function UserFileRoot: longint;
This function returns the root, or first record number, of the binary tree
that's used to structure the user file.
Programs which need to process users in alphabetical order, or which wish to
perform their own searches of the userfile, should use this function to
obtain the record number of the tree root. From this record, the pointers
'Leaf.Left' and 'Leaf.Right' (part of the UserType data structure) form a
binary tree, with 'Left' pointing to all users with names which come before
the current user alphabetically, and 'Right' pointing to all users who come
after. (The userfile should not, under normal circumstances, contain any
duplicate records).
The best way to access the user tree structure is through a recursive
traversal procedure. See the sample programs for examples of this
technique.
-----------------------------------------------------------------------------
Unit: SUBLIST.TPU
Depends On: DOS,General,Filedef,Block,Tree,Users
-----------------------------------------------------------------------------
Description:
This unit contains procedures for reading the Searchlight subboard list or
file directory list (ie. SUBBOARD.SL2 or FILEDIR.SL2) into memory, and
accessing its contents through your program.
The method Searchlight and the SUBLIST.TPU unit use to access the subboard
or file directory list is to read the entire file into memory as a linked
list, close the file, and access all information through memory pointers.
The functions in this unit allow you to read in the subboard list or file
directory list and provide data types and pointers for accessing the
contents in memory. Only one list can be loaded at any time.
Procedures, Functions and Variables:
type SubListPtr = ^SubListType;
SubListType = record { ram subboard/filedir list }
fname: string[8]; { name }
path: string[60]; { path to HDR/MSG/MBR or DIR file }
name: string[40]; { long description }
access: integer; { required access level }
attrib: attribset; { required attributes }
visible: boolean; { visible on lists? }
recnum: longint; { record # in SL2 file }
next: SubListPtr; { next subboard or filedir }
case integer of
1: ( { Subboards }
postattrib: attribset; { attributes required to post }
subsysop: string[25]; { subboard sysop }
echomail: boolean; { not currently supported }
lastread: longint); { not currently supported }
2: ( { File Directories }
filepath: string[38]; { path to upload/download files }
readonly,
writeonly: boolean; { readonly/writeonly status }
free: word; { free files (K) }
value: integer; { file value (x10) }
dirpath: boolean; { set if filepath<>path }
drive: integer); { drive where files reside (1=A:, etc.) }
end;
The 'SubListType' and 'SubListPtr' types define the data type used to store
records from either SUBBOARD.SL2 or FILEDIR.SL2 in memory. Typically,
application programs will access the list by setting a pointer of type
'SubListPtr' equal to the linked list root, then following Pointer^.Next to
access each successive record.
Notice that SubListType is a variant record, which holds data for either
subboard entried or file directory entries. It is up to the program to know
which variant is currently active, and not try to access data in the wrong
one.
'RecNum' provides the record number of the field within the SUBBOARD.SL2 or
FILEDIR.SL2 file. This value should NOT be used to modify the file. It is
provided for instances where you need to have a unique numerical identifier
for each subboard (for example, if you are writing a conversion procedure
for a BBS system which uses subboard numbers instead of names).
type SystemType = (Subboards,FileDirs);
const SubListRoot: SubListPtr = Nil;
var SubListSize: integer;
Type 'SystemType' is used by the 'SubListInit' procedure to specify whether
subboards or file directories are to be loaded. After execution of
SubListInit, 'SubListRoot' and 'SubListSize' are initialized to the root of
the subboard list and its size in entries.
Procedure SubListInit (system: systemtype);
This is the procedure that initializes the subboard list. The parameter is
either 'Subboards' or 'Filedirs'.
Example:
SubListInit(Subboards); { Initialize subboard list }
When called, the SUBBOARD.SL2 file is read into memory (memory is allocated
dynamically from the heap) and the SubListRoot and SubListSize variables are
initialized.
There is no need to explicitly deallocate the subboard list, as it is
automatically deallocated by an exit procedure when the program terminates.
Procedure SubListOrder (filename: string; system: systemtype);
This procedure rearranges the items in the subboard or file directory list
according to a custom ordering file, such as MAIN.SUB or MAIN.DIR. This
procedure should be called AFTER SubListInit. Pass the full filename to the
SUB/DIR file, and the system type (ie. Subboards or FileDirs). The result
is that the linked list is updated to be in the specified order.
If SubListOrder is not called, the linked list will be in alphabetical order
by default.
Note: when ordering the SUBBOARD list, the MAIL and BULLETIN subboards are
always included in the list, regardless of whether they appear in the
SUB/DIR file.
Function ListIndex (s: string): SubListPtr;
This function searches for a given entry (subboard name or filedir name) in
the linked list, and if found, returns a pointer to its record. If the item
is not found, the function returns a NIL pointer. The search string ('S')
should be all uppercase, and should not contain leading or trailing spaces.
Be aware that this function will attempt to match partial names as in
Searchlight BBS. For example, a call to ListIndex('FIDO') may return a
pointer to a subboard called 'FIDONET', if that subboard exists and one
called 'FIDO' does not. For critical applications, it is best to test the
name of the returned subboard explicity.
Example:
var p: sublistptr;
p:=ListIndex('MAIL');
if (p^.fname='MAIL')
then { ok }
else { error } ;
Sequential processing of the subboard/filedir list can be accomplished by
setting a pointer to the SubListRoot value initially, then following the
Next pointers until the end of the list is reached. Here is an example of a
function which will print out the names of all the subboards available:
var p: sublistptr;
begin
p:=SubListRoot;
while (p<>Nil) do begin
writeln(p^.Fname);
p:=p^.next;
end;
end;
Function SubIsReadable (s: sublistptr; var a: accesstype): boolean;
Determines whether the given subboard (indicated by a pointer to its
linked list memory location) is accessible to a user with the given
access levels. Typically, this function is used while processing a
subboard list to determine which subboards a user has permission to read.
Example:
if SubIsReadable(p,user.access)
then { process subboard }
else { skip subboard }
The actual formula used by this function is as follows:
SubIsReadable:=((s^.access<=a.msglevel) and (s^.attrib-a.attrib=[]))
or (a.msglevel>=254);
To check whether a user has POST access to a particular subboard, you can
use a similar formula, except comparing the user's attributes to the
subboard's 'PostAttrib' field. Notice the use of set subtraction in the
expression (s^.attrib-a.attrib=[]) as a means of determining whether the
user's attributes meet the attribute requirements of the subboard.
-----------------------------------------------------------------------------
Unit: MEMBERS.TPU
Depends On: General,Block,Filedef,Tree,Message
-----------------------------------------------------------------------------
Description:
Contains procedures for accessing the MEMBER files associated with
subboards, including procedures for searching, adding and deleting member
records.
The functions in MEMBERS.TPU work with the 'MainSub' variable declared in
MESSAGE.TPU, and require that the subboard member file be open (via a call
to OpenSub in MESSAGE.TPU).
Procedures, Functions and Variables:
var Member: MembType; { Global current member rec }
MembRec: longint; { Global member record number }
These global variables should be used to store the member record and record
number for the current user in the current subboard. It is recommended that
these variables not be used to store intermediate results when traversing a
member file (use local variables for that purpose). Member and MembRec are
not initialized when you open a subboard; however, they can easily be
initialized with one function call:
MSearch(user.name,member,membrec);
...Assuming USER is initialized to contain the current user's information.
Procedure ReadMember (var m: membtype; n: longint);
Procedure WriteMember (var m: membtype; n: longint);
These procedures read and write variables of type 'Membtype' from the MEMBER
file associated with the currently opened subboard. When writing, be sure
to observe file locking techniques as outlined in BLOCK.TPU.
Procedure Msearch ( key: string; { name to look for }
var mrec: membtype; { resulting user record }
var mpos: longint); { resulting pointer }
This procedure searches for a name in the member file. If the name is
found, it loads 'Mrec' with the member record, and sets 'Mpos' to the record
number where the member record was found. If the name is not found, then
'Mpos' contains a value of zero.
Procedure AddMember (var user: usertype;
var member: membtype;
var result: longint);
This procedure adds a member to the member file; it requires a USERTYPE
record containing the name of the user to be added. 'Member' need not be
initialized. The result contains the record number where the new member was
stored, or 0 if an error occured.
'AddMember' automatically initializes the fields of the member record with
the date of first login, and sets the high message pointer to zero.
If you wish to add a member who is not a user, create a dummy 'Usertype'
variable an initialize it with the desired member name (be sure to use all
capital letters).
Function DeleteMember (key: string): boolean;
Deletes a given member from the member file, returing TRUE if the delete was
successful, or FALSE if an error occured.
Function MemberFileRoot: longint;
This function returns the root record number of the binary tree structure
within a member file, and can be used to process the records of a member
file in alphabetical order. Searching member files is similar to searching
the user file. Please see 'UserFileRoot' in USERS.TPU and the example
programs for more information.
Procedure UpdateLastRead (n: longint);
This procedure updates the last read pointer for the current user record
(ie. the user record defined by the global variables 'Member' and
'Membrec'). 'N' is the new value to be assigned. This procedure performs
file locking as required for multiuser access.
-----------------------------------------------------------------------------
Unit: MESSAGE.TPU
Depends On: General,Block,Filedef,Sublist
-----------------------------------------------------------------------------
Description:
MESSAGE.TPU contains basic procedures required to open and manage subboards.
A major part of this unit is the description of the data structure used to
hold message texts in RAM; programs which read or write messages to
Searchlight subboards must be familiar with this structure since it is the
format which the post and retrieval commands expect.
This unit requires that the subboard list be loaded (SubListInit in
SUBLIST.TPU), and all functions that deal with reading or writing data from
subboards of course require that the subboard be opened first. Only one
subboard at a time can be opened with the procedures in this unit.
Procedures, Functions and Variables:
const maxlinlen = 76; { default max line length }
maxlines = 400; { max. lines per message }
type strptr = ^string;
msgptr = ^msgtype; { RAM Message Format }
msgtype = record
msglen: integer; { length of msg in lines }
msglin: array [1..maxlines]
of strptr; { pointers to lines }
end;
This is the data structure in which message texts are manipulated in memory.
Most procedures in this library expect a variable of type 'MsgPtr', which is
a pointer to a record containing the message length in lines, followed by an
array of pointers to the message lines. The lines themselves are stored in
memory as standard Turbo Pascal string types; conversion between this data
type and the format used to store messages on disk (including data
compression) is handled automatically by the library procedures.
Variables of type 'MsgPtr' need to be initialized, allocated and disposed of
in a strict manner using the functions below.
Procedure ClearMsg (msg: msgptr);
'ClearMsg' should be called with a new variable of type 'MsgPtr' right after
allocating heap space for that pointer; it creates a message with 0 lines.
This function MUST be called if you are creating a new message in memory.
Example:
var msg: msgptr;
New(msg); { allocate heap space for message }
ClearMsg(msg); { clear message to zero lines }
Procedure AddLine (var msg: msgptr; s: string);
This procedure adds a line to the given message, and can be used to build
messages in memory from external sources (such as a disk file, or an editor)
which will later be saved to a Searchlight message base. Be sure that the
'Msg' variable has been allocated and cleared (as above) before you call
this function. The line added must be 76 characters or less in length.
Programs which require editing of messages in memory should be aware that
the 'AddLine' function always allocates 78 bytes to each MsgLin variable in
the message; this insures that there is enough room for lines to be edited
if desired. If your editing procedure needs to insert or delete lines on
its own, you should use the GetMem and FreeMem procedures as follows:
GetMem(msg^.msglin[x],maxlinlen+2); { x = line number }
FreeMem(msg^.msglin[x],maxlinlen+2);
It is not necessary to use these functions unless you need to perform
extensive editing in memory. Otherwise, create the message by adding one
line at a time to it, and clear it with the 'DisposeMsg' function.
Procedure DisposeMsg (var msg: msgptr);
This procedure disposes a message completely, including deallocating the
space used by all the message lines and the space used by the 'Msgptr'
itself. Be sure to call this function when you are done with a message so
that its memory can be reused.
type SubFileSet = record { Files for message system }
headerf, { header file }
msgf, { texts file }
memberf: BlockFileType; { members file }
Name: string[8];
Subinfo: subtype;
end;
subfilestype = (HEADERF,MSGF,MEMBERF);
subfilesset = set of subfilestype;
const allfiles: subfilesset=[HEADERF,MSGF,MEMBERF];
var MainSub: SubFileSet;
These definitions form the in-memory data structures needed to declare and
open a subboard. 'MainSub' always contains information about the currently
open subboard, and is used implicitly by the message storage and retrieval
commands.
Function OpenSub (name: string; var f: SubFileSet; files: subfilesset): boolean;
The 'OpenSub' function attempts to open a subboard; it returns TRUE if the
subboard was opened, or FALSE if an error was encountered and the subboard
could not be initialized.
The 'Name' parameter contains the 1 to 8 character subboard name, which
should be all uppercase and have no leading or trailing spaces. 'F' should
always contain the value 'MainSub'. 'Files' contains the constant
'AllFiles' (defined above) or a subset of it, if you do not wish to open all
3 of the files associated with a subboard.
Example:
if OpenSub('GENERAL',MainSub,AllFiles) then begin
{ processing here }
end
else writeln('Could not open subboard!');
If the attempt to open the subboard is successful, the 'Name' and 'Subinfo'
fields in the 'SubFileSet' parameter are initialized with the name of the
subboard and the contents of the subboard's header information.
In general, the 'AllFiles' parameter should be used if you intend to do
general processing on a subboard. If you only intend to process the header
file, or if you do not need access to the Member file, you can save time by
opening only the files you need.
Example:
if OpenSub('GENERAL',Mainsub,[HEADERF]) then ... { Open Headerfile Only }
if OpenSub('GENERAL',Mainsub,[HEADERF,MSGF]) ... { Header & Message Files }
You MUST open all subboard files if you plan to save messages!
Procedure ReadSubInfo (var m: subfileset; var s: subtype);
Reads a subboard's information (header) record into memory. The variable
'Mainsub.Subinfo' is automatically initialized to contain the information
found in a subboard's header file when the header file is opened. You can
refer to this variable (which is of type 'Subtype') for information about
the current subboard. Be aware, however, that in multiuser systems,
information can change if the value is not refreshed from the actual disk
file; therefore, read it from disk before using any critical value.
Example:
ReadSubInfor(Mainsub,Mainsub.Subinfo);
writeln('There are ',Mainsub.Subinfo.Messages,' Messages on this subboard.');
Procedure CloseSub (var f: SubFileSet);
This procedure closes the open files in a subboard. Be sure to call this
when you are done processing a particular subboard, or when your program
ends.
Example:
CloseSub(Mainsub);
Function FindHeader (var h: headertype; ix: ixtype; id,rec: longint): longint;
'FindHeader' searches for the header record of a message given the message
number or UID number of the message. If the header is found, the function
returns its record number in the header file (of the currently open
subboard) and reads the header information into the 'Headertype' parameter.
The 'Ix' parameter is a constant which has the value 'Seq' or 'Uid'. Use
the 'Seq' value if you are searching for a message number, and use 'Uid' if
you are searching for a UID value (ie. the internal numbers used for
threading purposes). Please see the documentation file accompanying this
library for a more thourough discussion of message reading techniques.
The 'Id' value is a long integer containing either the message number or UID
number you are searching for. Note that if the header cannot be found, the
function returns a result of 0.
The 'Rec' parameter is optional; it can be used to indicate the record
number where you expect the header to be located. Pass a value of 0 if you
do not know this information. This parameter is included for instances
where you know the target record number, and saves you the trouble of
writing an IF-THEN statement to handle those cases. If the record number is
passed, the record can be located much more quickly.
Examples:
{ r = longint variable, header = headertype variable }
r:=FindHeader(header,Seq,1,0);
If r<>0 after this function call, 'Header' will contain the header of
message #1 on the current subboard, and R will contain its record number
in the header file. If r=0, then there is no message #1 on this subboard.
r:=FindHeader(header,Seq,100,RecNum);
Searches for message #100. If 'RecNum' is nonzero, the search is
expediated by looking directly to the record number indicated (for
example, this number might have been retrieved from the 'NextSeqRec' or
'NextMailRec' field of another header).
r:=FindHeader(header,Uid,header.replyto,0);
Assuming 'header' contains the header of a message which is a reply, this
function call locates the message to which that message is a reply.
Notice that 'Header.Replyto', as well as other thread values in the
header, represent Uid values rather than message number (Seq) values.
Procedure Unpackmsg (rec: longint; var msg: msgptr; newmsg: boolean);
This procedure retrieves the text of a message from the subboard message
file on disk into memory (in the 'Msg' variable). 'Rec' contains the record
number of the first block of text in the message. The value for 'Rec'
should always be obtained from the 'Txt' field of a valid header record.
The boolean variable 'Newmsg' should be set to TRUE if 'Msg' represents an
unitialized variable of type 'Msgptr'. If 'Msg' has been initialized (ie.
allocated with New(), and cleared with ClearMsg()), then this value can be
set to FALSE, and the text from the MSG file will append any text already in
memory in the Msg variable. Whether or not 'Msg' is initialized before
calling Unpackmsg, you MUST dispose of the message via DisposeMsg() when you
are done using it.
The 'Unpackmsg' procedure automatically determines whether a message has
been compressed and uncompresses and unpacks the text automatically.
Example:
Assume that 'Header' contains a valid header record from the header file:
Unpackmsg(header.txt,msg,true);
This procedure call will read the message's text into the memory variable
'Msg'. You can then access the text line by line through the 'Msg'
variable (see above for the record layout). Note that it is possible for
a message to have 0 lines of text.
Function IsSubop (var user: usertype): boolean;
This boolean function takes a Usertype record and returns TRUE if that user
is the SubSysop of the current subboard ('MainSub'); otherwise, it returns
FALSE.
Example:
if IsSubop(user)
then { grant subsysop priviledge }
Function Owns (var h: headertype; var user: usertype): boolean;
This function takes the header of a message and a Usertype as input, and
determines whether the user owns the message (ie. the user is the person who
posted the message). This function simply compares the name in the message
'From' field to the username.
Example:
if Owns(header,user)
then { allow user to edit message }
-----------------------------------------------------------------------------
Unit: POST.TPU
Depends On: Filedef,Block,Users,Members,Message,Kill
-----------------------------------------------------------------------------
Description:
The POST unit contains the complete high-level function calls necessary to
post a message on the currently opened subboard. This unit also provides a
quick function for adding members to subboards and for making duplicate
(carbon copy) messages.
Procedures, Functions and Variables:
Function MsgPost (msg: msgptr; { text to post }
var header: headertype; { header info }
original: longint; { original, if any }
verbose: boolean; { print 'Saving...' ? }
setuid: boolean): { compute uid? }
longint; { result record number }
The 'MsgPost' function is a complete high level function call for posting a
message on the current subboard. This function performs all of the
functions necessary for posting a message including allocating file space,
indexing the header file, and maintaining the linked lists and threads
between messages. Note that all 3 files of the current subboard must be
open before calling MsgPost.
Before calling MsgPost, you must:
(1) Have the text of the message you want to post in memory ('Msg')
(2) Initialize the header information
(3) If the message is a reply, know the UID number or message number
of the original message
Text should be contained in the 'Msg' variable, which is of type 'Msgptr' as
described in the MESSAGE.TPU unit. You can build the text portion of the
message using the ClearMsg and AddLine procedures from MESSAGE.TPU.
The message header (type HeaderType) should be initialized by filling the
header with binary zeros, then initializing the 'From', 'Touser', 'Subj',
'Time', and 'Date' fields accordingly. No other fields in the Header need
be initialized before calling MsgPost.
MsgPost automatically maintains reply threads. If the message is a reply,
the 'Original' parameter should contain the UID number of the original
message (this can be obtained from Header.Id[UID] in the header of the
original). If you know the message number of the original message instead
of its UID number, you can pass that value as a negative number (ie.
-1*Header.ID[Seq]). It is preferable to pass the UID number if possible
since the UID number is immune to any renumbering which might occur, and it
also allows preservation of threads when networking (for more information,
please see the DOC file). If your message is not a reply, pass 0 as the
value of this parameter.
In general, the 'SetUID' parameter should be passed as TRUE to have MsgPost
automatically compute the UID value of the message and store it in the
message header. If you are importing messages from a network and the UID
value of a message is known, you should store it in 'Header.Id[Uid]' and
pass FALSE for the SetUID parameter. This will allow the message to retain
its original UID value and provide for correct thread maintenance. See the
DOC file for more information.
If 'Verbose' is TRUE, MsgPost will print the message 'Saving...' as it saves
the message. Otherwise, no such message is displayed.
If applicable, you may set the 'FFrom' (forwarded-from), 'Rd' (times read),
'Prot' (purge protect) and 'Attribute' (Echomail attributes) fields of the
header before calling MsgPost.
'MsgPost' returns the record number in the header file where the header was
stored as a result. It also returns the Header itself initialized with the
message number of the new message, and other pointer information. Note that
all messages posted with MsgPost are always assigned the next highest
sequential message number on the current subboard.
MsgPost automatically compresses the text before posting the message, if
the compression option is selected for the current subboard.
Be sure to dispose of the text of the message ('DisposeMsg()') when you are
done with it.
Example:
{ In this example, we will read the text from a file called 'MYFILE.TXT'
and post it as a message from GUEST to SYSOP. Assume that a subboard
has been opened. }
{ prepare message text }
new(msg);
clearmsg(msg); { msg = msgptr }
assign(input,'MYFILE.TXT');
reset(input);
while not eof(input) do begin
readln(input,str); { str = string }
AddLine(msg,str);
end;
close(input);
{ prepare header }
fillchar(header,sizeof(header),0);
header.from:='GUEST';
header.touser:='SYSOP';
header.subj:='Text of Myfile.txt';
dosdate(header.date); { from GENERAL.TPU }
dostime(header.time); { from GENERAL.TPU }
{ post the message }
result:=MsgPost(msg,header,0,true,true); { result = longint }
Disposemsg(msg);
Note: if you wish to attach a file to the message, fill in the filename
in the header's 'fattach' field. Then copy the file into the system's
file attach directory, either before or immediately after saving the message.
(The file attach directory is defined in Cf.AttachPath).
Procedure MsgDup (msgtxt: longint; { msg to duplicate }
var header: headertype; { new header }
original: longint); { original message }
This procedure call allows you to make carbon copies of messages that have
been posted with MsgPost. It is recommended that this function be called
only immediately after MsgPost.
Calling this function will result in 2 messages on the subboard having
different headers but identical text. Note that Searchlight BBS performs
this function only in the MAIL subboard for the purpose of providing carbon
copies of one message to several users.
To make a carbon copy of a message, first create a new message header. The
new header should contain any changes (usually in the 'Touser' field) that
the copy requires.
'MsgTxt' is the record number in the MSG file where the text of the message
to duplicate begins. You can retrieve this value from 'Header.Txt' of the
original copy of the message.
Finally, if the message is a reply, pass the 'Original' value as in MsgPost.
MsgDup duplicates a message without making another copy of its text.
Therefore, it is fast and space efficient. You can call MsgDup as many
times as you need.
MsgDup cannot be used to copy a message from one subboard to another. To do
that, you must read the header and message into memory, open the new
subboard, and save the message with MsgPost.
Procedure MakeMember (username: string);
'MakeMember' is a quick procedure which adds the given username to the
member file of the current subboard, if the user is not already a member.
The 'Username' should be all caps, with no leading or trailing spaces.
It is recommended that this procedure be called before posting any messages
on the MAIL subboard, to insure that the user to whom the message is
addressed is actually a member (and therefore has a mailbox).
Example:
MakeMember(header.touser);
result:=MsgPost(msg,header,0,true,true);
Note that as an alternative to forcing the user to be a member before
posting MAIL, you could check whether the user is a member (see MEMBER.TPU)
before allowing the message to be posted. It is not necessary to force the
message recipient to be a member except on the MAIL subboard.
-----------------------------------------------------------------------------
Unit: KILL.TPU
Depends On: Filedef,Block,Message,Members,Users
-----------------------------------------------------------------------------
Description:
Contains procedures for deleting messages on the current subboard.
Procedures, functions and variables:
Procedure KillMsgID (msgid: longint);
This procedure deletes any message from the current subboard. 'Msgid' is
the message number of the message you want to delete (which can be obtained
from Header.Id[Seq] in the message's header, if not otherwise available).
KillMsgID automatically maintains all of the threading, linked lists, etc.
associated with the message. It also deletes any file that is attached to
the message (unless the 'Fretain' flag is set in the message header).
Example:
KillMsgID(100); { delete message #100 }
Procedure AutoTrim;
This procedure checks whether the number of messages on the current subboard
exceeds the maximum number of messages set by the Sysop in the SETUP
program. If it does, and if 'Auto purge old messages' is set to Yes, then
this procedure deletes the oldest unprotected messages on the subboard until
the total number of messages equals the maximum number. Files attached to
messages are also deleted.
AutoTrim can be called subsequent to posting a number of new messages on a
subboard, to insure that old messages are purged if required.
-----------------------------------------------------------------------------
Unit: CHAT.TPU
Depends On: Dos,Filedef,Block
-----------------------------------------------------------------------------
Description:
Contains functions to control the Searchlight BBS CHAT file.
This unit assumes that the CONFIG, NODES and CHAT file have already been
opened via the OpenFiles procedure (FILEDEF.TPU).
The Chat system maintains a queue of four incoming messages for each node on
the system in the CHAT.SL2 file, which is maintained by the procedures in
this unit.
Chat functions should only be used in a multiline environment (this can be
determined by checking that 'maxnode' has a value greater than 1).
Procedures, functions and variables:
Function MsgAvail: boolean;
This function returns true if an incoming message is available in our chat
queue, false if no message is available.
Example:
if MsgAvail
then { display incoming message }
Function GetMsg (var from,s: string): integer;
This function returns the next available message. The 'From' field is a
string representation of the sender's name, 'S' contains the message itself.
The function result is the node number where the message originated.
Example:
if MsgAvail then begin
msgfrom:=GetMsg(from,s);
writeln('Message from ',from,' on node ',msgfrom,':');
writeln(s);
end;
Function MsgFull (sendnode: integer): boolean;
This function checks if another node's message queue is full. If the queue
for the node indicated by 'sendnode' is full, the function returns TRUE,
otherwise it returns FALSE.
Message queues can hold up to 4 messages. If the user on a node to whom
messages are sent does not read those messages faster than they are being
sent, the queue can fill up. Use this function to determine whether a queue
is full before sending a message.
Example:
if not MsgFull(n)
then { send message to node N }
Function SendMsg (sendnode: integer; from,s: string): boolean;
This function sends a message to another node. 'Sendnode' is the node
number, 'From' is the name of the sender (up to 25 characters, usually the
current user name), and 'S' is the message, up to 73 characters.
The function result is TRUE if the message was sent, or FALSE if it was not
sent because the receiver's queue is full. When a message is not sent, the
application can either discard it, or offer the user a chance to wait a few
moments and send again.
Example:
If SendMsg(1,user.name,'Hello, World')
then writeln('Message send successfully')
else writeln('Message was not sent, queue was full');
Procedure ClearMsgQueue;
Clears the incoming message queue for the current node; in other words,
clears all messages to us. You can use this procedure when you want to
discard all incoming messages.
Function Tell (name,from,s: string): boolean;
This is a higher level function that sends a message to a user by name,
checking the NODES file to see whether that user is online. If the named
user is on another node, the message is sent and the function returns TRUE.
If the user is not online, or if the user's chat queue is full, no message
is sent and the return is FALSE.
Procedure SetChatStat (b: byte);
Sets the chat status for our node. Byte values are as follows:
0: 'Using Main Program'
1: 'Logging In'
2: 'In Chat Mode'
3: 'Chatting with SYSOP'
4: 'Reading Messages'
5: 'Entering a Message'
6: 'Using Files System'
7: 'Using Sysop Program'
50: 'Uploading a File'
51: 'Downloading a File'
52: 'Using a Door Program'
53: 'Logging Off'
99: 'Not Available'
Function GetChatStat (node: integer): byte;
Reads the chat status of another node. Values are the same as above.
Note that values 50 and above indicate that the user is away from the main
program (or not available); CHAT programs should refrain from initiating a
conversation with a user who's chat status is 50 or over.
Function ChatStatus (b: byte): string;
Returns a string representation of the given chat status. Strings returned
are as above.
-----------------------------------------------------------------------------
Unit: DIR.TPU
Depends On: Dos,Block,Filedef,Users,Tree,LList,Sublist,Message
-----------------------------------------------------------------------------
Description:
Contains procedures and functions for accessing Searchlight file directory
files, as well as general procedures useful for file processing.
Most of the procedures in this unit assume that the file directory list has
been initialized (via a call to SubListInit).
Procedures, functions and variables:
type DirFileSet = record
dirf: blockfiletype; { filename file }
descf: blockfiletype; { extended description file }
dirinfo: Dirheader;
name: string[8];
path: string[60];
drive: integer;
end;
var MainDir: dirfileset;
Variable 'MainDir' defines the current directory. Use this variable in
conjunction with the functions below which require a 'DirFileSet' parameter.
Function OpenDir (name: string; var f: DirFileSet): boolean;
Opens a file directory (*.SL2 and *.EDF files). 'Name' is the name of the
directory to open, which should be the exact name in all-caps with no spaces.
If the open is successful, the function returns TRUE.
Note that OpenDir requires that the list of file directories has already
been loaded into memory via SubListInit (SUBLIST.TPU) and that the requested
directory is part of the list currently in memory.
Example:
if OpenDir('UPLOADS',MainDir) then begin
{ process uploads dir }
end
else writeln('Could not open UPLOADS dir');
Procedure CloseDir (var f: DirFileSet);
Closes the current directory. Be sure to call this procedure to close the
data file when you are done processing it.
Example:
CloseDir(MainDir);
Procedure ReadDir (var d: dirtype; r: longint);
Procedure WriteDir (var d: dirtype; r: longint);
These procedures can be used to read and write records (type 'Dirtype') from
the current open file directory, when the record number is known.
'ReadDir' is generally of use when processing multiple files in order by
filename, date, or physical record order (if processing in physical order,
care must be taken to avoid processing deleted records, which are marked by
an $FF value in Leaf.Status). Use the DirFileRoot function (below) or the
ListRoot function (LLIST.TPU) for an appropriate starting point for
processing a directory in indexed order.
Use 'WriteDir' to update records. Be sure to observe proper file locking as
described in BLOCK.TPU. Do not alter the filename or date when writing a
record directly back to disk. If the filename or date must be changed, this
can be handled by deleting the record and re-inserting it (see below).
Example:
for i:=1 to SizeInRecs(Maindir.Dirf) do begin
ReadDir(dirrecord,i); { dirrecord = dirtype }
if Dirrecord.Leaf.status<>$FF
then { process record }
else { record is deleted }
end;
Procedure Dsearch ( key: string; { filename to look for }
var drec: dirtype; { resulting record }
var dpos: longint); { resulting pointer }
'DSearch' searches for a particular file by name in the current directory.
The input parameter (Key) should contain the filename in all caps, with no
spaces, drive letters, or directory paths.
On return, 'Drec' and 'Dpos' are set to the file's directory entry and
record number. If the record is not found, then Dpos=0.
Procedure AddFile (var newentry: dirtype; var result: longint);
This procedure call adds a new file to the current directory. You should
clear the 'Dirtype' parameter and then initialize it with the proper
filename, date, description and file length (note that the length is
expressed as the number of 128 byte blocks in the file, not the size in
bytes). The 'Id' field in Dirtype should contain the record number of the
user who uploaded the file, if applicable.
This procedure does not check whether the file actually exists. It is up to
the application program to make that determination before adding the record.
It is permissible to add a record for a file that does not exist, if that is
desired.
Be sure to initialize the password field of the record before inserting it.
See procedure Hash in GENERAL.TPU for more information.
Example:
fillchar(newentry,sizeof(dirtype),0);
with newentry do begin
name:='MYFILE.ZIP';
descrip:='Private file for the sysop';
length:=GetFileSize('MYFILE.ZIP') div 128; { see below }
id:=cf.curruser;
fillchar(passwd,sizeof(passwd),0); { Blank password field }
Dosdate(date); { DosDate from GENERAL.TPU }
end;
AddFile(newentry,result);
Note: if you want to store an extended description with the file, you should
call SaveDescription immediately prior to AddFile. See text below.
Function DeleteFile (key: string): boolean;
This function deletes the given entry from the Searchlight file directory.
'Key' should contain the exact filename in all caps. The result is TRUE if
the file was successfully deleted, FALSE if the file was not found or could
not be deleted because of a disk error.
This procedure does not affect the physical file, only its directory entry.
If you wish to delete the actual file, you must do so yourself.
Example:
if DeleteFile('MYFILE.ZIP')
then writeln('Deleted successfully')
else writeln('Error deleting file.');
Note: this deletes the file's extended description text, if any.
Function DirFileRoot: longint;
Returns the record number of the root of the binary tree in the current
directory file. Can be used to process the file directory in alphabetical
order; the procedure is much the same as for processing the User file. See
example programs for more information.
The following functions may be useful to application programs when
processing user input, etc.
Function IsWild (var s: string): boolean;
This function returns TRUE if the input filename contains any wildcard
characters or if it contains two or more filenames separated by a space. If
the input consists of a single filename, FALSE is returned.
It is up to application programs to parse wildcard entries, if such
processing is desired.
Function Legal (filename: string): boolean;
Returns TRUE if the given filename is a legal DOS filename and contains no
drive letters, paths, or wildcards. Otherwise, returns FALSE. This
function is useful for checking user input filenames before attempting to
access the file, to protect against accessing illegal filenames.
Function GetFileSize (filename: string): longint;
Returns the size of a given disk file in bytes. This function accesses the
file itself on disk, not the Searchlight directory entry. 'Filename' may be
any filename including a path, if required.
The result is the file size in bytes or 0 if the file does not exist.
Extended Descriptions
Each file in Searchlight 3.5 can have an "extended" file description of
up to 400 lines associated with it. Searchlight stores these extended
descriptions by creating a new file for each directory -- the new file
ends with the extension '.EDF', and is stored in the same place as the
directory's .SL2 file. The .EDF file stores text in the same format as a
subboard .MSG file, including text compression, and is therefore a highly
efficient way of storing file descriptions of varying lengths. The .EDF
file is created automatically when a directory is opened, if it does not
already exist.
An extended file description is indicated by a pointer in the message header:
EdfTxt: longint; { pointer to extended description }
If this pointer is nonzero, it indicates that an extended description exists
for the file. The following functions are included in the DIR unit for
handing extended descriptions:
Procedure GetDescription (d: longint; var msg: msgptr);
Function SaveDescription (msg: msgptr): longint;
Procedure KillDescription (d: longint);
Notice that extended descriptions are saved and retrieved into variables of
type MsgPtr -- the same type of variable that is used for storing and
reading messages. To get an extended file description, call GetDescription
with the EdfTxt pointer and an uninitialized variable of type msgptr:
GetDescription(filerecord.edftxt,msg);
The resulting message variable contains the text of the extended description.
For more details about the MsgPtr type, see examples of retrieving messages
in the FILEDEF.REF document and the sample programs.
Note: Check to see if the MsgPtr variable is Nil after a call to this
function; if so, proceed as though no description is available. The call
could return a nil value if the EdfTxt pointer contained garbage, such
as if the record was saved by a program which was not aware of this pointer
and did not clear its value to zero before saving the record.
Remember to dispose of the extended description text when you are done with
it, by calling the DisposeMsg function. This frees the memory used to store
the description text.
The SaveDescription function stores an extended file description in the
directory's EDF file. Pass the function a variable of type MsgPtr that has
been built to contain the description text. The result is a pointer, which
should be loaded directly into the message header. Note that this function
allows you to handle description texts separately from files: it is up to
you to make sure you manage the descriptions and files together. The correct
way to add a file to the Searchlight directory with an extended description:
(1) Create the data record for the file and the description text.
(2) Save the description text with the SaveDescription function. Store
the result in the EdfTxt field of the data record.
(3) Add the data record to the directory file (AddFile function).
The KillDescription function is necessary only if you wish to edit an
existing extended description. It's not necessary to explicitly call this
function before deleting a file, because the DeleteFile function calls it
if necessary. When editing a file with an extended description, the proper
procedure is:
(1) Load the old description into memory and edit it.
(2) Kill the old description with the KillDescription function.
(3) Save the modified description with SaveDescription. Store the
result of the function in the file's data record.
(4) Save the file's data record back to disk (so that the updated
pointer is saved).
If you are replacing the description with something completely new, then
there is no need to load the old description.
Note: If you are saving or adding a file without an extended description,
be sure to store a value of 0 in the EdfTxt field.
Old vs. New descriptions: Searchlight 3.5 still uses the "short" file
description (40 character description in the message header). The old
two-line extended description (field 'Edescrip') is no longer used to
store descriptions, but is supported when reading old directories. In order
to maintain compatibility with both old and new directory formats:
(1) When reading files, check for an extended description in EdfTxt
first. If one is present, use it, and ignore the contents of the
old Edescrip fields.
(2) When saving or updating files, always store extended descriptions with
the SaveDescription function, not in the Edescrip fields. Do this even
if the extended description is only one or two lines. If updating an
old entry, copy the data from the Edescrip field into a MsgPtr type
variable, and re-save it as an extended description.
-----------------------------------------------------------------------------
Unit: TREE.TPU
Depends On: Block,Filedef
-----------------------------------------------------------------------------
Description:
Contains low level procedures and functions associated with maintenance of
binary tree data structures.
The only function in this unit which is of use to application programs is:
Function TreeSize (var f: blockfiletype): longint;
This function returns the number of records allocated to the binary tree,
for binary tree oriented files (such as the User file or subboard member
files). This tells you the actual number of active records in the file.
Example:
writeln('There are ',TreeSize(Userfile),' Active users on file.');
Notice the difference between TreeSize and SizeInRecs (BLOCK.TPU): TreeSize
returns the number of records in use, whereas SizeInRecs returns the number
of records physically allocated in the file, including deleted records and
records which have never been used. TreeSize is valid only for binary tree
oriented files (Userfile, Member files [MAINSUB.MEMBERF] and Dir files).
-----------------------------------------------------------------------------
Unit: LLIST.TPU
Depends On: General,Block,Filedef,Tree
-----------------------------------------------------------------------------
Description:
Contains low level procedures associated with the maintenance of linked
lists in directory files.
The only procedure in this unit which may be needed in application programs
is:
Function ListRoot (var f: blockfiletype): longint;
Returns the root of the date-sorted linked list for a file directory. This
procedure can be used to process files in date sorted order. The return
value is always the most recently uploaded file in the current directory.
To traverse the date tree, follow 'Leaf.Next' and 'Leaf.Last' pointers in
the directory records.
Example:
root:=ListRoot(Maindir.Dirf);
-----------------------------------------------------------------------------
Unit: MULTI.TPU
Depends On: Dos
-----------------------------------------------------------------------------
Description:
Contains procedures which help optimize the performance of a program when
running under multitasking operating systems. You can call the procedures
in this unit from any program, and they will work regardless of what
operating system is running.
Procedures, functions and variables:
type OSType = (SingleUser,DoubleDos,DesqView,WinStd,WinEnh,OS2);
Var Environment: OSType;
The 'Environment' variable is initialized automatically by MULTI.TPU's
startup code, and can be examined to determine whether your program is
running under one of the supported multitasking operating systems. Note:
'WinStd' indicates Windows running in either Standard or Real mode.
'WinEnh' indicates Windows running in 386 enhanced mode.
Example:
case Environment of
DoubleDos: writeln('We are running under DoubleDos');
DesqView: writeln('We are running under DesqView');
WinStd: writeln('We are running in standard or real mode Windows');
WinEnh: writeln('We are running in Windows Enhanced 386 mode');
OS2: writeln('We are running in OS/2');
SingleUser: writeln('We are not running under a known multitasker');
end;
Note: If Environment=Singleuser it does not mean that no other nodes are
active; it only means that no multitasker that this until can detect is
running. The system could be running on a LAN or under a multitasker which
is not detected. Always check the 'MaxNode' variable (FILEDEF.TPU) to
determine the number of active nodes.
Procedure Slice; { slice to next process }
The 'Slice' procedure is very important for programs that contain loops
which wait for keystrokes or other external events. When running under a
multitasker, calling 'Slice' causes the multitasker to begin running the
next program immediately, thus allocating a minimum of CPU time to your
program. This is desirable when your program is idle for long periods of
time. 'Slice' is usually called in keyboard input loops, but can also be
called from any loop which waits for something to happen (such as a loop
that waits for a modem carrier).
Example:
while not KeyPressed do Slice;
{ waste time in an efficient manner until a keystroke occurs }
Procedure Pause (n: integer); { delay for n 55-ms intervals }
This procedure pauses for a given number of clock ticks. One clock tick
is equal to approximately 55 milliseconds. 'Pause' calls the 'Slice'
procedure repeatedly until the required time has elapsed, thus your program
uses very little CPU time during the pause.
'Pause' should be called anytime your program needs to pause; it works
regardless of whether a multitasking OS is loaded.
Example:
Pause(18); { Pause for about 1 second }
Note that the accuracy of this procedure is plus or minus 1 clock tick.
-----------------------------------------------------------------------------
Unit: STYLEDEF.TPU
Depends On: Block, Filedef, General
-----------------------------------------------------------------------------
Description:
Contains procedures for accessing the RIP styles (RIPSTYLE.SL2 file) and
RIP general setup parameters. Also includes a style stack, so that programs
can push and pop the current RIP style.
Procedures, functions and variables:
Const MaxStyles = 16; { Number of available styles }
This is the number of styles available in a RIPSTYLE.SL2 file.
Var RipStyle: RipStyleType; { Global RIP style record }
RipCf: StyleHeaderType; { RIP General Setup }
CurrStyle: integer; { Currently set style number }
'RipStyle' is a global variable that's used to store the current RIP style.
Once a current style is selected, programs can read the RipStyle variable
to implement the current style. 'CurrStyle' stores the record number (1
through MaxStyles) of the current style. 'RipCf' stores the global RIP
General Setup information. Check the STYLEDEF.REF file for the formats of
these data types.
Procedure RipStyleInit;
This procedure opens the RIPSTYLE.SL2 file, loads the first style and the
General Setup information into memory, and clears the style stack. You must
open the CONFIG file before calling this procedure. Note: the RipStyleInit
function will create a new RIPSTYLE.SL2 file (filled with binary zeros) if
one does not exist when the function call is made.
Procedure CloseStyleFile;
Call this procedure before your program exits. It closes the style file.
Procedure GetRipStyle (n: integer);
This procedure loads a particular RIP style into memory (ie. loads it into
the global 'RipStyle' variable declared in this unit). The parameter 'n'
must be between 1 and MaxStyles. Note: this call simply loads the style
number indicated, and does not save the old style. For stack operations,
use the functions below.
Procedure ClearStyleStack; { Clears style stack }
Procedure SetRipStyle (n: integer); { Loads style, saves old style on stack }
Procedure ResetRipStyle; { Restores old style from stack }
These three functions set up a style stack that allows you to load new styles
into memory while keeping track of old ones. Call the ClearStyleStack
function first. To load a new style into memory while keeping track of the
old one, call SetRipStyle. Then, call ResetRipStyle to load the previous
style back into memory. The stack contains room for 60 nested calls.
Procedure SaveRipStyle (n: integer); { Save global style to style file }
If you wish to update a style, call this function. It saves the global
RipStyle variable into record number 'n' of the RIPSTYLE file.
Procedure WriteStyleHeader; { Update style file header }
If you wish to update any of the RIP General Setup parameters, make this
function call after modifying the global RipCf variable.
-----------------------------------------------------------------------------
(c) Copyright 1993 Searchlight Software. All Rights Reserved.